package com.feifanxuexi.userservice.service;

import com.feifanxuexi.userservice.domain.Role;
import com.feifanxuexi.userservice.domain.User;
import com.feifanxuexi.userservice.repo.RoleRepo;
import com.feifanxuexi.userservice.repo.UserRepo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * NOTE: 因为我们需要在security中使用数据库中的用户名和密码信息，所以有这个服务实现中，
 * 添加了实现<em>UserDetailsService</>这个接口。
 */
@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class UserServiceImpl implements UserService, UserDetailsService {

    private final UserRepo userRepo;
    private final RoleRepo roleRepo;
    private final PasswordEncoder passwordEncoder;

    /**
     * 这是UserDetailService要求的实现的方法，security需要使用这个方法对用户进行认证。
     *
     * @param username 需要认证的用户
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepo.findByUsername(username);
        if (user == null) {
            // 如果没有数据库中找到，抛出一个异常。
            log.error("user {} not find in the database", username);
            throw new UsernameNotFoundException("User not find exception ");
        } else {
            //如果找到了用户，写一个日志信息
            log.info("在数据库中找到了用户: {}", user.getName());
        }

        // 需要返回一个Security使用的UserDetail信息,
        // 所以我们将数据库中的用户角色信息转换为security需要使用对象
        // 实际上，authorities可以支持任何类型的GrantedAuthority
        Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
        user.getRoles().forEach(role ->
                authorities.add(new SimpleGrantedAuthority(role.getName()))
        );

        // 需要返回一个Security使用的UserDetail信息,
        return new org.springframework.security.core.userdetails.User(
                user.getUsername(),
                user.getPassword(),
                authorities
        );

    }

    @Override
    public User saveUser(User user) {
        //NOTE 查找不能工作的原因,  后来又自己正常了。 可能是maven开始时没有正常工作的结果。
        log.info("Saving new user {} to database", user.getName());
        //用户的密码需要编码
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return userRepo.save(user);
    }

    @Override
    public Role saveRole(Role role) {
//        log.info("Saving new role {} to the database", role.getName());
        return roleRepo.save(role);
    }

    @Override
    public void addRoleToUser(String username, String roleName) {
//        log.info("A new Role {} to be add to user {username} ", roleName, username);
        User user = userRepo.findByUsername(username);
        Role role = roleRepo.findByName(roleName);
        user.getRoles().add(role);
    }

    @Override
    public User getUser(String username) {

        return userRepo.findByUsername(username);
    }

    @Override
    public List<User> getUsers() {

        return userRepo.findAll();
    }

}
